/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ytmc.downloader;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.FileConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import ytmc.setup.Config;
import ytmc.FileSize;
import ytmc.Model;
import ytmc.TransferSpeed;

/**
 *
 * @author K
 */
public class DownloaderModel extends AbstractTableModel implements Model
{
    private String[] columnNames = { "Wideo", "Status", "Rozmiar", "Postęp", "Prędkość", "Szacowane" };
    private Class[] columnClasses = { String.class, FileSize.class, String.class, String.class, TransferSpeed.class , String.class  };
    private ArrayList<DownloadTask> tasks;

    public DownloaderModel()
    {
        this.tasks = new ArrayList<DownloadTask>();
        loadTasks();
    }

    public void addTask(DownloadTask task)
    {
        final DownloadTask fianlTask = task;
        tasks.add(fianlTask);
        int rowNumber = tasks.size() - 1;
        fireTableRowsInserted(rowNumber, rowNumber);

        DownloadTaskDataRetriever dataRetriever = new DownloadTaskDataRetriever(task);
        dataRetriever.addPropertyChangeListener(new PropertyChangeListener()
        {
            @Override
            public void propertyChange(PropertyChangeEvent evt)
            {
                if(evt.getPropertyName().equals("finished") && (Boolean)evt.getNewValue() == true)
                {
                    fireTableRowsUpdatedOnDispatcher(fianlTask);
                    saveTask(fianlTask);
                }
            }
        });
        dataRetriever.retrieveAsync();
    }

    public void removeTask(int index)
    {
        if( index >= 0 && index < tasks.size() )
        {
            tasks.remove(index);
            fireTableRowsDeleted(index, index);
            saveTasks();
        }
    }

    public void startTask(int index)
    {
        if(index >= 0 && index < tasks.size())
        {
            final DownloadTask task = tasks.get(index);
            if(!task.isComplete())
            {
                task.addPropertyChangeListener(new PropertyChangeListener()
                {
                    @Override
                    public void propertyChange(PropertyChangeEvent evt)
                    {
                        if(evt.getPropertyName().equals("complete") && (Boolean)evt.getNewValue() == true)
                        {
                            try
                            {
                                SwingUtilities.invokeAndWait(new Runnable()
                                {
                                    @Override
                                    public void run()
                                    {
                                        downloadAsync(task);
                                    }
                                });
                            }
                            catch (InterruptedException ex)
                            {
                                Logger.getLogger(DownloaderModel.class.getName()).log(Level.SEVERE, null, ex);
                            }
                            catch (InvocationTargetException ex)
                            {
                                Logger.getLogger(DownloaderModel.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                    }
                });
            }
            else
            {
                downloadAsync(task);
            }
        }
    }

    private void downloadAsync(final DownloadTask task)
    {
        DownloadTaskRunner runner = new DownloadTaskRunner(task);
        runner.addPropertyChangeListener(new PropertyChangeListener()
        {
            @Override
            public void propertyChange(PropertyChangeEvent evt)
            {
                if(evt.getPropertyName().equals("task"))
                {
                    fireTableRowsUpdatedOnDispatcher(task);
                }
                else if (evt.getPropertyName().equals("finished"))
                {
                    saveTask(task);
                }
            }
        });

        runner.downloadAsync();
    }

    private void fireTableRowsUpdatedOnDispatcher(final DownloadTask task)
    {
        try
        {
            SwingUtilities.invokeAndWait(new Runnable()
            {
                @Override
                public void run()
                {
                    int index = tasks.indexOf(task);
                    fireTableRowsUpdated(index, index);
                }
            });
        }
        catch (InterruptedException ex)
        {
            Logger.getLogger(DownloaderModel.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (InvocationTargetException ex)
        {
            Logger.getLogger(DownloaderModel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void saveTask(DownloadTask task)
    {
        XMLConfiguration config = Config.load();

        try
        {
            int i = tasks.indexOf(task);

            DownloadTaskStatus status = task.getStatus();
            if (status != DownloadTaskStatus.FINISHED)
            {
                status = DownloadTaskStatus.NOT_STARTED;
            }

            config.setProperty("download.tasks.task{" + i + "}.URL", task.getUrl());
            config.setProperty("download.tasks.task{" + i + "}.direct-video-URL", task.getDirectVideoURL());
            config.setProperty("download.tasks.task{" + i + "}.path", task.getSavePath());
            config.setProperty("download.tasks.task{" + i + "}.title", task.getTitle());
            config.setProperty("download.tasks.task{" + i + "}.size", task.getSize().getSize());
            config.setProperty("download.tasks.task{" + i + "}.progress", task.getProgress());
            config.setProperty("download.tasks.task{" + i + "}.status", status);
            config.setProperty("download.tasks.task{" + i + "}.is-complete", task.isComplete());

            config.save();
        }
        catch (ConfigurationException ex)
        {
            Logger.getLogger(DownloaderModel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void saveTasks()
    {
        XMLConfiguration config = Config.load();

        try
        {
            config.clearTree("download.tasks");

            int i = 0;
            for (DownloadTask task : tasks)
            {
                DownloadTaskStatus status= task.getStatus();
                if (status != DownloadTaskStatus.FINISHED)
                {
                    status = DownloadTaskStatus.NOT_STARTED;
                }

                config.setProperty("download.tasks.task{" + i + "}.URL", task.getUrl());
                config.setProperty("download.tasks.task{" + i + "}.direct-video-URL", task.getDirectVideoURL());
                config.setProperty("download.tasks.task{" + i + "}.path", task.getSavePath());
                config.setProperty("download.tasks.task{" + i + "}.title", task.getTitle());
                config.setProperty("download.tasks.task{" + i + "}.size", task.getSize().getSize());
                config.setProperty("download.tasks.task{" + i + "}.progress", task.getProgress());
                config.setProperty("download.tasks.task{" + i + "}.status", status);
                config.setProperty("download.tasks.task{" + i + "}.is-complete", task.isComplete());

                i++;
            }

            config.save();
        }
        catch (ConfigurationException ex)
        {
            Logger.getLogger(DownloaderModel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void loadTasks()
    {
        tasks.clear();
        FileConfiguration config = Config.load();

        try
        {
            List savedTasks = config.getList("download.tasks.task.URL");
            if (savedTasks != null)
            {
                for(int i = 0; i < savedTasks.size(); i++)
                {
                    String url = config.getString("download.tasks.task{" + i + "}.URL");
                    String path = config.getString("download.tasks.task{" + i + "}.path");

                    DownloadTask task = new DownloadTask(url, path);

                    task.setUrl(config.getString("download.tasks.task{" + i + "}.URL"));
                    task.setDirectVideoURL(config.getString("download.tasks.task{" + i + "}.direct-video-URL"));
                    task.setTitle(config.getString("download.tasks.task{" + i + "}.title"));
                    task.setSize(new FileSize(config.getLong("download.tasks.task{" + i + "}.size")));
                    task.setProgress(config.getInt("download.tasks.task{" + i + "}.progress"));
                    task.setStatus(DownloadTaskStatus.valueOf(config.getString("download.tasks.task{" + i + "}.status")));
                    task.setComplete((config.getBoolean("download.tasks.task{" + i + "}.is-complete")));

                    tasks.add(task);
                }
            }
        }
        catch (Exception ex)
        {
            Logger.getLogger(DownloaderModel.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public Class<?> getColumnClass(int i)
    {
        return columnClasses[i];
    }

    @Override
    public String getColumnName(int i)
    {
        return columnNames[i];
    }

    @Override
    public int getRowCount()
    {
        return tasks.size();
    }

    @Override
    public int getColumnCount()
    {
        return columnNames.length;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex)
    {
        if( rowIndex < 0 || rowIndex >= getRowCount() )
        {
            return null;
        }

        switch(columnIndex)
        {
            case 0:
                return tasks.get(rowIndex).getTitle();
            case 1:
                return tasks.get(rowIndex).getStatus().getFriendlyName();
            case 2:
                return tasks.get(rowIndex).getSize();
            case 3:
                return String.format("%d %%", tasks.get(rowIndex).getProgress());
            case 4:
                return tasks.get(rowIndex).getDownloadSpeed();
            case 5:
                return tasks.get(rowIndex).getRemainingTime().toString();
            default:
                return null;
        }
    }
}
